#pragma once
#include <iostream>
#include <sys/epoll.h>
#include <string.h>
#include "Log.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"

using namespace NetSockModule;
using namespace EpollModule;

class EpollServer
{
    const static int default_backlog = 16;
    const static int num = 32; // 一次读取就绪fd个数
public:
    EpollServer(int port)
        : _port(port), _listensock(new TcpSocket()), _isruning(false), _epoller(new Epoller())
    {
    }
    void InitServer()
    {
        _listensock->BuildListenSocketMethod(_port, default_backlog);
        _epoller->InitEpoll();                                 // 创建epoll模型
        _epoller->AddEvent(_listensock->GetSockFd(), EPOLLIN); // 添加节点
    }
    void Loop()
    {
        _isruning = true;
        while (_isruning)
        {
            _epoller->PrintList(); // for test
            int timeout = 1000;
            int n = _epoller->EpollWait(_reps, num, timeout);
            switch (n)
            {
            case 0:
                lg(Info, "epoll wait timeout...");
                break;
            case -1:
                lg(Error, "epoll wait error, service end");
                Stop();
                break;
            default:
                HandlerEvent(n);
                break;
            }
        }
        _isruning = false;
    }
    void Stop()
    {
        _isruning = false;
    }
    ~EpollServer()
    {
    }

private:
    ssize_t Recver(int sockfd, std::string *out)
    {
        char buff[1024];
        ssize_t n = ::recv(sockfd, buff, sizeof(buff) - 1, 0);
        if (n > 0)
        {
            buff[n] = 0;
            *out += buff;
        }
        return n;
    }
    void HandlerEvent(int n)
    {
        lg(Info, "epoll wait success handler... 有%d个文件描述符就绪了", n);
        for (int i = 0; i < n; i++)
        {
            // 判断这个是否是自己需要关心的事件
            if (_reps[i].events & EPOLLIN)
            {
                int fd = _reps[i].data.fd;
                // 判断是普通还是listenfd
                if (fd == _listensock->GetSockFd())
                {
                    std::string clientip;
                    uint16_t clientport;
                    int sockfd = _listensock->AcceptSocketOrDie(&clientip, &clientport);
                    if (sockfd < 0)
                    {
                        continue;
                    }
                    lg(Info, "get a new link [%s:%d]", clientip.c_str(), clientport);
                    // 这里怎么处理？？？？？？？当然是直接托管交给epoll来管理
                    _epoller->AddEvent(sockfd, EPOLLIN);
                }
                else
                {
                    std::string message;
                    ssize_t n = Recver(fd, &message);
                    if (n > 0)
                    {
                        std::cout << "client say#  " << message << std::endl;
                        if (message.size() > 2)
                            message.resize(message.size() - strlen("\r\n"));

                        std::string echo_message = "hello epoll server  : " + message + "\r\n";
                        ::send(fd, echo_message.c_str(), echo_message.size(), 0);
                    }
                    else
                    {
                        if (n == 0)
                        {
                            lg(Info, "client quit...... fd close: %d", fd);
                        }
                        else
                        {
                            lg(Error, "call recv failed fd close: %d", fd);
                        }
                        _epoller->DelEvent(fd); // 先删除rbtree节点再close fd
                        ::close(fd);
                    }
                }
            }
        }
    }

private:
    std::unique_ptr<Socket> _listensock;
    std::unique_ptr<Epoller> _epoller;
    int _port;
    bool _isruning;
    struct epoll_event _reps[num];
};